import { SpotI, RectI, AngleI, ArcI, Color, ColorString, CompositeType, CursorType, FontI, ImageI, ImagePointI, ImageRectI, RoundRectI } from "./CanvasType";
import WatchNumber from '@/utils/WatchNumber';

/** 画板类 */
export default class CanvasUtils {

	/** 画板元素 */
	private element: HTMLCanvasElement;

	/** 画布类 */
	private ctx: CanvasRenderingContext2D;

	/** 宽度 */
	private width: WatchNumber = new WatchNumber(-1);

	/** 高度 */
	private height: WatchNumber = new WatchNumber(-1);

	/** 画布类 */
	public get Ctx(): CanvasRenderingContext2D {
		return this.ctx;
	}

	/** 画板类 */
	public get Element(): HTMLCanvasElement {
		return this.element;
	}

	/**
	 * 构造函数
	 * @param element 画板元素
	 */
	public constructor(element: HTMLCanvasElement) {
		this.element = element;
		const ctx = element.getContext("2d");
		if (!ctx) {
			throw new Error("获取画板失败");
		}
		this.ctx = ctx;
		this.AutoResize();
	}

	/** 界面自适应 */
	public AutoResize() {
		const element = this.element;
		this.Width.Add(null, () => { element.width = this.Width.Value; });
		this.Height.Add(null, () => { element.height = this.Height.Value; })
		this.Width.Value = element.offsetWidth;
		this.Height.Value = element.offsetHeight;
	}

	/** 宽度 */
	public get Width(): WatchNumber {
		return this.width;
	}

	/** 高度 */
	public get Height(): WatchNumber {
		return this.height;
	}

	/** 宽度 */
	public get Cursor(): CursorType {
		return this.element.style.cursor as CursorType;
	}

	public set Cursor(val: CursorType) {
		this.element.style.cursor = val;
	}

	/** 描边颜色 */
	public get StrokeStyle(): Color {
		return this.ctx.strokeStyle;
	}

	public set StrokeStyle(val: Color) {
		this.ctx.strokeStyle = val;
	}

	/** 填充颜色 */
	public get FillStyle(): Color {
		return this.ctx.fillStyle;
	}

	public set FillStyle(val: Color) {
		this.ctx.fillStyle = val;
	}

	/** 透明度 */
	public get GlobalAlpha(): number {
		return this.ctx.globalAlpha;
	}

	public set GlobalAlpha(val: number) {
		this.ctx.globalAlpha = val;
	}

	/** 线条宽度 */
	public get LineWidth(): number {
		return this.ctx.lineWidth;
	}

	public set LineWidth(val: number) {
		this.ctx.lineWidth = val;
	}

	/** 线条两端样式 */
	public get LineCap(): CanvasLineCap {
		return this.ctx.lineCap;
	}

	public set LineCap(val: CanvasLineCap) {
		this.ctx.lineCap = val;
	}

	/** 线条连接样式 */
	public get LineJoin(): CanvasLineJoin {
		return this.ctx.lineJoin;
	}

	public set LineJoin(val: CanvasLineJoin) {
		this.ctx.lineJoin = val;
	}

	/** 线条虚线样式 */
	public get LineDash(): number[] {
		return this.ctx.getLineDash();
	}

	public set LineDash(val: number[]) {
		this.ctx.setLineDash(val);
	}

	/** 文字样式 */
	public get Font(): string {
		return this.ctx.font;
	}

	public set Font(val: string) {
		this.ctx.font = val;
	}

	/** 文本左右对齐 */
	public get TextAlign(): CanvasTextAlign {
		return this.ctx.textAlign;
	}

	public set TextAlign(val: CanvasTextAlign) {
		this.ctx.textAlign = val;
	}

	/** 文本基线 */
	public get TextBaseline(): CanvasTextBaseline {
		return this.ctx.textBaseline;
	}

	public set TextBaseline(val: CanvasTextBaseline) {
		this.ctx.textBaseline = val;
	}

	/** 文字方向 */
	public get Direction(): CanvasDirection {
		return this.ctx.direction;
	}

	public set Direction(val: CanvasDirection) {
		this.ctx.direction = val;
	}

	/** 重叠方式 */
	public get GlobalCompositeOperation(): CompositeType {
		return this.ctx.globalCompositeOperation as CompositeType;
	}

	public set GlobalCompositeOperation(val: CompositeType) {
		this.ctx.globalCompositeOperation = val;
	}

	/** 文字样式 */
	public get FontFamily(): string {
		const res = /px (.*?)$/.exec(this.ctx.font);
		return res === null ? "" : res[1];
	}

	public set FontFamily(val: string) {
		this.ctx.font = this.ctx.font.replace(/px (.*?)$/, `px ${val}`);
	}

	/** 文字大小 */
	public get FontSize(): number {
		const res = /^([\d.]{1,})px/.exec(this.ctx.font);
		return res === null ? 0 : parseInt(res[1]);
	}

	public set FontSize(val: number) {
		this.ctx.font = this.ctx.font.replace(/^([\d.]{1,})px/, `${val}px`);
	}

	/**
	 * 创建线性渐变
	 * @param param0 渐变点A
	 * @param param1 渐变点B
	 */
	public CreateLinearGradient({ X: AX, Y: AY }: SpotI, { X: BX, Y: BY }: SpotI): CanvasGradient {
		return this.ctx.createLinearGradient(AX, AY, BX, BY);
	}

	/** 保存绘画状态 */
	public Save(): void {
		this.ctx.save();
	}

	/** 返回到保存绘画状态 */
	public Restore(): void {
		this.ctx.restore();
	}

	/**
	 * 位移
	 * @param x x位移量
	 * @param y y位移量
	 */
	public Translate(x: number, y: number): void {
		this.ctx.translate(x, y);
	}

	/**
	 * 旋转
	 * @param angle 要旋转的角度
	 */
	public Rotate(angle: AngleI): void {
		this.ctx.rotate(angle.Value);
	}

	/**
	 * 缩放
	 * @param x x缩放倍数
	 * @param y y缩放倍数
	 */
	public Scale(x: number, y: number): void {
		this.ctx.scale(x, y);
	}

	/**
	 * 矩阵变换
	 * @param a a值
	 * @param b b值
	 * @param c c值
	 * @param d d值
	 * @param e e值
	 * @param f f值
	 */
	public Transform(a: number, b: number, c: number, d: number, e: number, f: number): void {
		this.ctx.transform(a, b, c, d, e, f);
	}

	/**
	 * 裁剪
	 * @param fillRule 裁剪规则
	 */
	public Clip(fillRule?: "nonzero" | "evenodd" | undefined): void {
		this.ctx.clip(fillRule);
	}

	/** 开始描绘路径 */
	public BeginPath(): void {
		this.ctx.beginPath();
	}

	/** 闭合路径 */
	public ClosePath(): void {
		this.ctx.closePath();
	}

	/**
	 * 移动点
	 * @param param0 点参数
	 */
	public MoveTo({ X, Y }: SpotI): void {
		this.ctx.moveTo(X, Y);
	}

	/**
	 * 连点路径
	 * @param param0 点参数
	 */
	public LineTo({ X, Y }: SpotI): void {
		this.ctx.lineTo(X, Y);
	}

	/**
	 * 描贝塞尔路径
	 * @param param0 参考点
	 * @param param1 点参数
	 */
	public QuadraticCurveTo({ X: cpX, Y: cpY }: SpotI, { X, Y }: SpotI): void {
		this.ctx.quadraticCurveTo(cpX, cpY, X, Y);
	}

	/**
	 * 描圆路径
	 * @param param0 圆参数
	 */
	public Arc({ Spot: { X, Y }, Radius, StartAngle, EndAngle, Counterclockwise }: ArcI): void {
		this.ctx.arc(X, Y, Radius, StartAngle.Value, EndAngle.Value, Counterclockwise);
	}

	/**
	 * 描绘曲线路径
	 * @param param0 参考点
	 * @param param1 点参数
	 * @param Radius 半径
	 */
	public ArcTo({ X: aX, Y: aY }: SpotI, { X: bX, Y: bY }: SpotI, Radius: number): void {
		this.ctx.arcTo(aX, aY, bX, bY, Radius);
	};

	/** 描边 */
	public Stroke(): void {
		this.ctx.stroke();
	}

	/** 填充 */
	public Fill(): void {
		this.ctx.fill();
	}

	/**
	 * 填充矩形
	 * @param param0 矩形参数
	 */
	public FillRect({ Spot: { X, Y }, Width, Height }: RectI): void {
		this.ctx.fillRect(X, Y, Width, Height);
	}

	/**
	 * 描边矩形
	 * @param param0 矩形参数
	 */
	public StrokeRect({ Spot: { X, Y }, Width, Height }: RectI): void {
		this.ctx.strokeRect(X, Y, Width, Height);
	}

	/**
	 * 填充描边矩形
	 * @param rect 矩形参数
	 */
	public FillStrokeRect(rect: RectI): void {
		this.FillRect(rect);
		this.StrokeRect(rect);
	}

	/**
	 * 画渐变矩形
	 * @param rect 矩形参数
	 * @param pointA 渐变点A
	 * @param pointB 渐变点B
	 * @param colorList 渐变颜色组
	 */
	public GradientRect(rect: RectI, pointA: SpotI, pointB: SpotI, ...colorList: ColorString[]): void {
		const gradient = this.CreateLinearGradient(pointA, pointB);
		const length = colorList.length - 1;
		colorList.forEach((color, i) => { gradient.addColorStop(i / length, color); });
		this.FillStyle = gradient;
		this.FillRect(rect);
	}

	/**
	 * 画圆角矩形
	 * @param param0 圆角矩形参数
	 */
	public RoundRect({ Rect: { Spot: { X, Y }, Width, Height }, Radius, BackgroundColor, BorderColor }: RoundRectI): void {
		const leftTop = { X: X, Y: Y };
		const rightTop = { X: X + Width, Y: Y };
		const rightBottom = { X: X + Width, Y: Y + Height };
		const leftBottom = { X: X, Y: Y + Height };
		if (Radius < 0) Radius = 0;
		if (Width >= Radius * 2) {
			this.BeginPath();
			this.MoveTo({ X: X + Radius, Y });
			this.ArcTo(rightTop, rightBottom, Radius);
			this.ArcTo(rightBottom, leftBottom, Radius);
			this.ArcTo(leftBottom, leftTop, Radius);
			this.ArcTo(leftTop, rightTop, Radius);
			if (BackgroundColor) { this.Fill(); }
			if (BorderColor) { this.Stroke(); }
		} else {
			this.BeginPath();
			this.MoveTo(rightBottom);
			this.ArcTo(leftBottom, leftTop, Radius);
			this.ArcTo(leftTop, rightTop, Radius);
			this.LineTo(rightTop);
			this.Clip();
			this.BeginPath();
			this.MoveTo(leftTop);
			this.ArcTo(rightTop, rightBottom, Radius);
			this.ArcTo(rightBottom, leftBottom, Radius);
			this.LineTo(leftBottom);
			this.Clip();
			if (BackgroundColor) {
				this.Fill();
			}
			if (BorderColor) {
				this.Stroke();
				this.BeginPath();
				this.MoveTo(rightBottom);
				this.ArcTo(leftBottom, leftTop, Radius);
				this.ArcTo(leftTop, rightTop, Radius);
				this.LineTo(rightTop);
				this.Stroke();
			}
		}
	}

	/**
	 * 填充文本
	 * @param param0 文本参数
	 */
	public FillText({ Spot: { X, Y }, Text, MaxNumber }: FontI): void {
		this.ctx.fillText(Text, X, Y, MaxNumber);
	}

	/**
	 * 描边文本
	 * @param param0
	 */
	public StrokeText({ Spot: { X, Y }, Text, MaxNumber }: FontI): void {
		this.ctx.strokeText(Text, X, Y, MaxNumber);
	}

	/**
	 * 填充描边文本
	 * @param font 文字参数
	 */
	public FillStrokeText(font: FontI): void {
		this.FillText(font);
		this.StrokeText(font);
	}

	/**
	 * 绘制图片
	 * @param param0 图片参数
	 */
	public DrawImage(param: ImageI): void {
		if ((param as ImagePointI).Spot) {
			const { Image, Spot: { X, Y } } = param as ImagePointI;
			this.ctx.drawImage(Image, X, Y);
		} else if ((param as ImageRectI).Rect) {
			const { Image, Rect: { Spot: { X, Y }, Width, Height } } = param as ImageRectI;
			this.ctx.drawImage(Image, X, Y, Width, Height);
		}
	}

	// 圆角图片
	public RoundImage(Rect: RectI, Image: CanvasImageSource, Radius: number): void {
		if (Radius < 0) { Radius = 0; }
		const { X, Y } = Rect.Spot;
		this.BeginPath();
		this.Arc({
			Spot: { X: X + Radius, Y: Y + Radius },
			Radius, StartAngle: { Value: 0 }, EndAngle: { Value: Math.PI * 2 }
		});
		this.Clip();
		this.DrawImage({ Image, Rect });
	}

	// 取文本对象
	public GetTextWidth(val: string): number {
		return this.ctx.measureText(val).width;
	}

	/**
	 * 获取矩形区域图片
	 * @param param0 矩形参数
	 */
	public GetImageData({ Spot: { X, Y }, Width, Height }: RectI): ImageData {
		return this.ctx.getImageData(X, Y, Width, Height);
	}

	/**
	 * 绘入图片数据
	 * @param imagedata 图片数据
	 * @param dx x位置
	 * @param dy y位置
	 */
	public PutImageData(imagedata: ImageData, dx: number, dy: number): void {
		this.ctx.putImageData(imagedata, dx, dy);
	}

	// 描绘背景
	public FillBackground(color: Color) {
		this.FillStyle = color;
		const Spot = { X: 0, Y: 0 };
		const { Width: { Value: Width }, Height: { Value: Height } } = this;
		this.FillRect({ Spot, Width, Height });
	}

	// 绘制背景图片
	public FillBackgroundImage(Image: CanvasImageSource): void {
		const Spot = { X: 0, Y: 0 };
		const { Width: { Value: Width }, Height: { Value: Height } } = this;
		this.DrawImage({ Image, Rect: { Spot, Width, Height } });
	}

	/**
	 * 清空矩形区域
	 * @param param0 矩形参数
	 */
	public ClearRect({ Spot: { X, Y }, Width, Height }: RectI): void {
		this.ctx.clearRect(X, Y, Width, Height);
	}

	/** 清除全部 */
	public Clear(): void {
		const Spot = { X: 0, Y: 0 };
		const { Width: { Value: Width }, Height: { Value: Height } } = this;
		this.ClearRect({ Spot, Width, Height });
	}
}
